/**
 * Client.cpp
 * Greg Steltenpohl & Cory Bryant
 */

#include <arpa/inet.h>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <netdb.h>
#include <netinet/in.h>
#include <pthread.h>

#include "randNum.h"
#include "Blowfish.h"
#include "Client.h"
#include "Packet.h"
#include "PracticalSocket.h"

#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <vector>
#include <queue>

using namespace std;

map<int, int> errors;



void mainFTP(Client*, char*);


/*
 * CompareTimeout is a class which acts as a comparator for packets in the window vector
 * which are pointed to by timeoutQueue.  Highest priority is the packet with the lowest timeout timestamp.
 */
// @TODO: Make sure the correct thread is on top.
class CompareTimeout {
	public:
	bool operator()(const Packet* p1, const Packet* p2) // Returns true if p1 is smaller than p2
	{
		if (p1->timeout.tv_sec < p2->timeout.tv_sec) return true;
		if (p1->timeout.tv_sec == p2->timeout.tv_sec && p1->timeout.tv_nsec < p2->timeout.tv_nsec) return true;
		return false;
	}
};

// Will be replaced by calls to client->get___(); of some sort client->getServer(___)->getwindowSize()
// Unless returned value is 0, in which case use a default.
int senderWinSize; // 1 = sw
int recvWinSize; // 1 = gbn or sw, >1 sr. Always < senderWinSize
int sequenceNumberRange;
int timeoutInterval; // nanoseconds
int packetSize; // Allow extra padding for encryption
char* servHostName;
int serverPort;

// These variables control the window and numbers
int seqNum = 0;
int packNum = 0;
int expectedSeqNum = 0;

// Vector representing the current window.
vector<Packet*> window(senderWinSize, NULL);
// PriorityQueue of pointers to the same locations as above.
// Used to determine timeouts
priority_queue<Packet*, vector<Packet*>, CompareTimeout> timeoutQueue;

// Buffer for reading in from file
char* buffer;
// File for reading in what is to be parsed
// @TODO ADD file name to send Start.txt, 1 for each connection, or lazy way is to make all transfer same file?
// We could leave as command line but I dun know about that
ifstream myFile;
// encryption object
Blowfish bf;

// socket to send/recv on
UDPSocket sock;
// servAddress and port will be known from other client
string servAddress;
unsigned short servPort;

// Mutex for controling access to window Objects
pthread_mutex_t windowMutex = PTHREAD_MUTEX_INITIALIZER; 
// mutex for controlling when to move the window and add new packets
pthread_mutex_t senderMutex = PTHREAD_MUTEX_INITIALIZER;

// Methods
void* threadRecv(void*);
void* threadTimer(void*);
timespec diff(timespec, timespec);

/*
 * printClientID prints out the current pid+hostip.
 */
void printClientID(Client* client) {
	// Print my idA.
	char* idA = new char[INNER_BUFFER_LENGTH];
	memset(idA, 0, INNER_BUFFER_LENGTH);
	// Get host name.
	char* myHostName = new char[INNER_BUFFER_LENGTH];
	memset(myHostName, 0, INNER_BUFFER_LENGTH);
	gethostname(myHostName, INNER_BUFFER_LENGTH);
	// Get ip address.
	char* ipAddress = inet_ntoa(*(in_addr*)gethostbyname(myHostName)->h_addr);
	// Remove periods.
	char* ip1 = strtok(ipAddress, ".");
	char* ip2 = strtok(NULL, ".");
	char* ip3 = strtok(NULL, ".");
	char* ip4 = strtok(NULL, ".");
	// Get PID.
	int pid = getpid();
	// Append all togeather.
	sprintf(idA, "%d%s%s%s%s", pid, ip1, ip2, ip3, ip4);
	// Print out my idA.
	cout << "My idA is " << idA << "\nType any (reasonable) thing and press enter to begin simulation.\n>";
	client->setIdA(idA);
	cin >> myHostName;
	// Free Resources.
	delete[] myHostName;
}

/*
 * Get an error type by sequenceNumber.
 * Returns an error type int if successful, 0 if not found for no error.
 */
int getErrorType(int sequenceNumber) {
	map<int, int>::iterator iter = errors.find(sequenceNumber);
	if (iter != errors.end()) {
		return iter->second;
	} else {
		return NO_ERR;
	}
}

/*
 * Main function pulls data from file and user, and begins simulation.
 */
int main(int argc, char *argv[]) {
	ifstream starterFile ("Start.txt"); // Starter file reading from.
	char* line = new char[256]; // Line in file.
	memset(line, 0, 256);
	char* token = new char[256]; // First token of line.
	memset(token, 0, 256);
	char* delimiter = "="; // Tokenizer delimiter.
	char* keyA;
	char* nonceA;
	Client* client;
	char* serverHostName;
	char* idB;
	Server* server;
	char* idA;
	char* ekb;
	int i;
	char* filePath;
	// Get from file.
	if (starterFile.is_open()) {
		// Get next line with "encryption".
		do {
			starterFile.getline(line, 256);
			token = strtok(line, delimiter);
		} while (strcmp(token, "encryption"));
		// Get encryption.
		int encryption = atoi(strtok(NULL, delimiter));
		// Get next line with "filePath".
		filePath = new char[INNER_BUFFER_LENGTH];
		memset(filePath, 0, INNER_BUFFER_LENGTH);
		do {
			starterFile.getline(line, 256);
			token = strtok(line, delimiter);
		} while (strcmp(token, "filePath"));	
		// Get filePath.
		sprintf(filePath, "%s", strtok(NULL, delimiter));
		// Get next line with "protocol".
		char* protocol = new char[INNER_BUFFER_LENGTH];
		memset(protocol, 0, INNER_BUFFER_LENGTH);
		do {
			starterFile.getline(line, 256);
			token = strtok(line, delimiter);
		} while (strcmp(token, "protocol"));	
		// Get protocol.
		sprintf(protocol, "%s", strtok(NULL, delimiter));
		// Get next line with "sequenceNumberRange".
		do {
			starterFile.getline(line, 256);
			token = strtok(line, delimiter);
		} while (strcmp(token, "sequenceNumberRange"));
		// Get sequenceNumberRange.
		sequenceNumberRange = atoi(strtok(NULL, delimiter));
		// Get next line with "packetSize".
		do {
			starterFile.getline(line, 256);
			token = strtok(line, delimiter);
		} while (strcmp(token, "packetSize"));
		// Get packetSize.
		packetSize = atoi(strtok(NULL, delimiter)) + PADDING_SIZE;
		// Get next line with "timeoutInterval".
		do {
			starterFile.getline(line, 256);
			token = strtok(line, delimiter);
		} while (strcmp(token, "timeoutInterval"));
		// Get timeoutInterval.
		timeoutInterval = atoi(strtok(NULL, delimiter));
		// Get errors.
		// Get next line with "numberOfErrors".
		do {
			starterFile.getline(line, 256);
			token = strtok(line, delimiter);
		} while (strcmp(token, "numberOfErrors"));
		// Get numberOfErrors.
		int numberOfErrors = atoi(strtok(NULL, delimiter));
		// If user defined errors.
		if (numberOfErrors > 0) {
			for (i = 0; i < numberOfErrors; i++) {
				// Get next line with "errorType".
				char* errorType = new char[INNER_BUFFER_LENGTH];
				memset(errorType, 0, INNER_BUFFER_LENGTH);
				do {
					starterFile.getline(line, 256);
					token = strtok(line, delimiter);
				} while (strcmp(token, "errorType"));
				// Get errorType.
				sprintf(errorType, "%s", strtok(NULL, delimiter));
				// Get next line with "sequenceNumber".
				do {
					starterFile.getline(line, 256);
					token = strtok(line, delimiter);
				} while (strcmp(token, "sequenceNumber"));
				// Get sequenceNumber.
				int sequenceNumber = atoi(strtok(NULL, delimiter));
				int errorTypeInt = NO_ERR;
				if (strcmp(errorType, "LP")) {
					errorTypeInt = LOST_PACK;
				} else if (strcmp(errorType, "LA")) {
					errorTypeInt = LOST_ACK;
				} else if (strcmp(errorType, "DP")) {
					errorTypeInt = CORR_PACK;
				}
				// add to errors map<sequence, type>
				errors.insert(pair<int, int>(sequenceNumber, errorTypeInt));
			}
		} else if (numberOfErrors < 0) { // Random errors
			// Get next line with "randomPercent".
			do {
				starterFile.getline(line, 256);
				token = strtok(line, delimiter);
			} while (strcmp(token, "randomPercent"));
			// Get randomPercent.
			int randomPercent = atoi(strtok(NULL, delimiter));
			srand(time(NULL));
			for (i = 0; i < sequenceNumberRange; i++) {
				if (randomPercent - 1 > rand() % 100) {
					int errorTypeInt = NO_ERR;
					int randomCheck = rand() % 100;
					if (24 < randomCheck) {
						errorTypeInt = LOST_PACK;
					} else if (49 < randomCheck) {
						errorTypeInt = LOST_ACK;
					} else if (74 < randomCheck) {
						errorTypeInt = CORR_PACK;
					}
					// add to errors map<sequence, type>
					errors.insert(pair<int, int>(i, errorTypeInt));
				}
			}
		}
		// Get next line with "hostName".
		char* kdcHostName = new char[INNER_BUFFER_LENGTH];
		memset(kdcHostName, 0, INNER_BUFFER_LENGTH);
		do {
			starterFile.getline(line, 256);
			token = strtok(line, delimiter);
		} while (strcmp(token, "hostName"));	
		// Get kdcHostName.
		sprintf(kdcHostName, "%s", strtok(NULL, delimiter));
		// Get next line with "port".
		do {
			starterFile.getline(line, 256);
			token = strtok(line, delimiter);
		} while (strcmp(token, "port"));
		// Get port.
		int kdcPort = atoi(strtok(NULL, delimiter));
		// Get my client number from user.
		int clientNumber = 0;
		cout << "Please enter the client number for this client.\n>";
		cin >> clientNumber;
		// Get next line with "clientNumber=clientNumber".
		int clientNumberBool = 0;
		int tokenNumber = 0;
		do {
			clientNumberBool = 0;
			starterFile.getline(line, 256);
			token = strtok(line, delimiter);
			if (!strcmp(token, "clientNumber")) {
				clientNumberBool = 1;
				token = strtok(NULL, delimiter);
				tokenNumber = atoi(token);
			}
		} while (!(clientNumberBool == 1 && tokenNumber == clientNumber));
		// Get next line with "keyA"
		keyA = new char[INNER_BUFFER_LENGTH];
		memset(keyA, 0, INNER_BUFFER_LENGTH);
		do {
			starterFile.getline(line, 256);
			token = strtok(line, delimiter);
		} while (strcmp(token, "keyA"));
		// Get keyA.
		sprintf(keyA, "%s", strtok(NULL, delimiter));
		// Get next line with "nonceA"
		nonceA = new char[INNER_BUFFER_LENGTH];
		memset(nonceA, 0, INNER_BUFFER_LENGTH);
		do {
			starterFile.getline(line, 256);
			token = strtok(line, delimiter);
		} while (strcmp(token, "nonceA"));
		// Get nonceA.
		sprintf(nonceA, "%s", strtok(NULL, delimiter));
		// Get next line with "windowSize".
		do {
			starterFile.getline(line, 256);
			token = strtok(line, delimiter);
		} while (strcmp(token, "windowSize"));
		// Get windowSize.
		int clientWindowSize = atoi(strtok(NULL, delimiter));
		senderWinSize = clientWindowSize;
		// Get next line with "numberOfConnections".
		do {
			starterFile.getline(line, 256);
			token = strtok(line, delimiter);
		} while (strcmp(token, "numberOfConnections"));
		// Get numberOfConnections.
		int numberOfConnections = atoi(strtok(NULL, delimiter));
		// Create a new client.
		client = new Client(encryption, kdcHostName, kdcPort, protocol, sequenceNumberRange, packetSize, timeoutInterval, keyA, nonceA, numberOfConnections, clientWindowSize);
		// Get each connection for connections map.
		for (i = 0; i < numberOfConnections; i++) {
			// Get next line with "serverNumber"
			do {
				starterFile.getline(line, 256);
				token = strtok(line, delimiter);
			} while (strcmp(token, "serverNumber"));
			int serverNumber = atoi(strtok(NULL, delimiter));
			// Get next line with "hostName"
			serverHostName = new char[INNER_BUFFER_LENGTH];
			memset(serverHostName, 0, INNER_BUFFER_LENGTH);
			do {
				starterFile.getline(line, 256);
				token = strtok(line, delimiter);
			} while (strcmp(token, "hostName"));
			sprintf(serverHostName, "%s", strtok(NULL, delimiter));
			// Get next line with "port"
			do {
				starterFile.getline(line, 256);
				token = strtok(line, delimiter);
			} while (strcmp(token, "port"));
			// Get serverPort.
			serverPort = atoi(strtok(NULL, delimiter));
			// Get next line with "windowSize"
			do {
				starterFile.getline(line, 256);
				token = strtok(line, delimiter);
			} while (strcmp(token, "windowSize"));
			// Get serverWindowSize.
			int serverWindowSize = atoi(strtok(NULL, delimiter));
			recvWinSize = serverWindowSize;
			// Get idB.
			idB = new char[INNER_BUFFER_LENGTH];
			memset(idB, 0, INNER_BUFFER_LENGTH);
			cout << "Please enter the idB for server number " << serverNumber << ".\n>";
			cin >> idB;
			// Create new Server object.
			server = new Server(serverHostName, serverPort, serverWindowSize);
			// Add server to client's server list.
			client->addServer(idB, server);
		}
		// Close file.
		starterFile.close();
		// Promt for file name
	} else {
		cout << "FILE ERROR" << endl;
		exit(1);
	}

	// Print idA.
	printClientID(client);

	// For every connection to be made, make a connection.
	for (i = 0; i < client->getNumberOfConnections(); i++) {
		try {
			// Establish connection with the kdc.
			TCPSocket servSock(client->getKdcHostName(), client->getKdcPort());
			// Create the request message to send to the kdc.
			char* requestBuffer = new char[INNER_BUFFER_LENGTH];
			memset(requestBuffer, 0, INNER_BUFFER_LENGTH);
			char* serverID = (client->getServerIDs())[i];
			sprintf(requestBuffer, "%s|%s|%s|", client->getIdA(), serverID, client->getNonceA());
			// Send the string to the kdc
			servSock.send(requestBuffer, strlen(requestBuffer));
			cout << "Sent to KDC:\n\tidA|idB|nonceA|\n\t" << requestBuffer << endl;
			// Get back the response.
			delete[] requestBuffer;
			char* responseBuffer = new char[MAX_BUFFER_LENGTH];
			memset(responseBuffer, 0, MAX_BUFFER_LENGTH);
			servSock.recv(responseBuffer, MAX_BUFFER_LENGTH);
			// Decrypt if encryption is enabled.
			Blowfish blowfish;
			if (client->getEncryptionOn() == 1) {
				// Decrypt the message.
				blowfish.Set_Passwd(client->getKeyA());
				blowfish.Decrypt(responseBuffer, (!(MAX_BUFFER_LENGTH % 8 == 0) ? MAX_BUFFER_LENGTH + 8 - (MAX_BUFFER_LENGTH % 8) : MAX_BUFFER_LENGTH));
			}
			cout << "Recd from KDC:\n\t" << responseBuffer << endl;
			// Parse response
			// enc[keyS|idA|idB|nonceA|enc[keyS|idA|]|]
			// Get keyS and print
			char* keyS = new char[INNER_BUFFER_LENGTH];
			memset(keyS, 0, INNER_BUFFER_LENGTH);
			sprintf(keyS, "%s", strtok(responseBuffer, "|"));
			client->setKeyS(keyS);
			cout << "Decrypted Ks:\n\t" << keyS << endl;
			// Get idA, idB, and nonceA to be sure they are they same
			idA = new char[INNER_BUFFER_LENGTH];
			memset(idA, 0, INNER_BUFFER_LENGTH);
			sprintf(idA, "%s", strtok(NULL, "|"));
			idB = new char[INNER_BUFFER_LENGTH];
			memset(idB, 0, INNER_BUFFER_LENGTH);
			sprintf(idB, "%s", strtok(NULL, "|"));
			nonceA = new char[INNER_BUFFER_LENGTH];
			memset(nonceA, 0, INNER_BUFFER_LENGTH);
			sprintf(nonceA, "%s", strtok(NULL, "|"));
			if (atoi(idA) != atoi((client->getIdA()))) {
				cout << "idA is incorrect" << endl;
			} else if (atoi(idB) != atoi(serverID)) {
				cout << "idB is incorrect" << endl;
			} else if (atoi(nonceA) != atoi(client->getNonceA())) {
				cout << "nonceA is incorrect" << endl;
			}
			delete[] idA;
			// Get part to send to Server
			ekb = new char[MAX_BUFFER_LENGTH];
			memset(ekb, 0, MAX_BUFFER_LENGTH);
			if (client->getEncryptionOn() == 1) {
				sprintf(ekb, "%s", strtok(NULL, "|"));
			} else {
				// Parse differently
				// keyS|idA|idB|nonceA|keyS|idA||
				char* testA = new char[INNER_BUFFER_LENGTH];
				char* testB = new char[INNER_BUFFER_LENGTH];
				sprintf(testA, "%s", strtok(NULL, "|"));
				sprintf(testB, "%s", strtok(NULL, "|"));
				sprintf(ekb, "%s|%s|", testA, testB);
			}
			// Send to idB.
			Server* server = client->getServer(serverID);
			servHostName = server->getServHostName();
			TCPSocket serverSocket(server->getServHostName(), server->getServPort());
			// Send the ekb
			serverSocket.send(ekb, strlen(ekb));
			if (client->getEncryptionOn() == 1) {
				cout << "Sent to " << serverID << ":\n\tE(Kb)[Ks|idA|]\n\t" << ekb << endl;
			} else {
				cout << "Sent to " << serverID << ":\n\tKs|idA|\n\t" << ekb << endl;
			}
			delete[] responseBuffer;
			// Get the response handshake
			char* handshakeBuffer = new char[INNER_BUFFER_LENGTH];
			memset(handshakeBuffer, 0, INNER_BUFFER_LENGTH);
			serverSocket.recv(handshakeBuffer, INNER_BUFFER_LENGTH);
			// if encryption is enabled.
			if (client->getEncryptionOn() == 1) {
				blowfish.Set_Passwd(client->getKeyS());
				blowfish.Decrypt(handshakeBuffer, (!(INNER_BUFFER_LENGTH % 8 == 0) ? INNER_BUFFER_LENGTH + 8 - (INNER_BUFFER_LENGTH % 8) : INNER_BUFFER_LENGTH));
				cout << "Recd from " << idB << ":\n\tE(Ks)[nonceB]\n\t" << handshakeBuffer << endl;
			}
			char* replyBuffer = new char[INNER_BUFFER_LENGTH];
			memset(replyBuffer, 0, INNER_BUFFER_LENGTH);
			sprintf(replyBuffer, "%ld", randNum((long) atoi(handshakeBuffer)));
			cout << "\tf(nonceB)= " << replyBuffer << endl;
			delete[] handshakeBuffer;
			if (client->getEncryptionOn() == 1) {
				blowfish.Set_Passwd(client->getKeyS());
				blowfish.Encrypt(replyBuffer, (!(INNER_BUFFER_LENGTH % 8 == 0) ? INNER_BUFFER_LENGTH + 8 - (INNER_BUFFER_LENGTH % 8) : INNER_BUFFER_LENGTH));
			}
			serverSocket.send(replyBuffer, INNER_BUFFER_LENGTH);
			cout << "Sent to " << idB << ":\n\tE(Kb)[nonceB]\n\t" << replyBuffer << endl;
			delete[] replyBuffer;
			serverSocket.send(filePath, 128);
			// Destructor closes the socket
		} catch (SocketException &e) {
			cerr << e.what() << endl;
			exit(1);
		}
	}
	// Start ftp.
	mainFTP(client, filePath);
	return 0;
}

void mainFTP(Client* client, char* filePath) {
	buffer = new char[packetSize - HEADER_SIZE];

	// Get server address and file name to send
	servAddress = servHostName;
	char* file = filePath;

	// Seems odd to have the this here, particularly when it is no where else in the code
	// I would make an iterator when/if it was necessary.
	vector<Packet*>::iterator windowIt;

	// @TODO: Be sure to print out everything Tan asks for int the writeup.
	cout << "Configuring timeout" << endl;
	// Get the average timeout.
	// This is cool, I didn't know this was do-able.
	FILE *p = popen("ping -c 3 ec2-50-16-179-130.compute-1.amazonaws.com", "r"); // @TODO Change
	if (!p)	{
		perror("popen failed");
	}
	char pbuffer[256] = { 0 };
	while (fgets(pbuffer, sizeof(pbuffer), p)) {
		string out(pbuffer, 3);
		if (out.compare("rtt") == 0) {
			char temp[5];
			memcpy(temp, pbuffer + 29, 5);
			timeoutInterval = (int) (atof(temp) * 400 * 1000000); // ns
			cout << timeoutInterval << endl;
		}
	}
	if (p) pclose(p);

	// Get port and resolve socket, argv3 is port from other client.]
	char* temp = new char[4];
	memcpy(temp, &serverPort, sizeof(serverPort));
	servPort = Socket::resolveService(temp);

	// Set encryption key
	// client->getKeyS()
	bf.Set_Passwd(client->getKeyS());
	
	// Begin ftp activities.
	try {
		// open file for sending.
		myFile.open(file, ios::in | ios::binary);
		
		// Split off reciever thread
		// Handles ack recv, setting ack received.
		pthread_t threadID; // Thread ID from pthread_create().
		if (pthread_create(&threadID, NULL, threadRecv, NULL) != 0) {
			cerr << "Unable to create thread" << endl;
			exit(1);
		}

		// Split off timer thread
		pthread_t timerThreadID; // Thread ID from pthread_create().
		if (pthread_create(&timerThreadID, NULL, threadTimer, NULL) != 0) {
			cerr << "Unable to create thread" << endl;
			exit(1);
		}

		bool done = false;

		// Fill window and send files
		// Loop thru window and send any that have not recieved acks or have not been sent
		while (!done) {
			if (myFile.is_open() && !myFile.eof()) {
				// block until ack received for first window frame
				pthread_mutex_lock(&senderMutex);
				// Fill window with packets and send them 
				pthread_mutex_lock(&windowMutex);

				// Global timeout for GBN
				timespec gbnTimeout;
				clock_gettime(CLOCK_REALTIME, &gbnTimeout);
				for (int index = 0; index < senderWinSize; index++) {
					if ((window.at(index) == NULL || !(*window.at(index)).sent) && !myFile.eof()) {
						// @TODO: Make this work with the reset method you gots
						// @TODO: Problem, on reset/new, the old pointer thing must be removed from the timeout que
						// @TODO: Therefore, in the timeout, we will need to do a check for the reset or null pointer
						// in that way we will be able to add it to the queue normally below, and just toss aside any bad packet pointers/any packet that has been reset since being added to the queue in the timeout thread.
						memset(buffer, 0, packetSize - HEADER_SIZE);
						myFile.read(buffer, packetSize - HEADER_SIZE - PADDING_SIZE);
						short sizeRead = myFile.gcount();

						Packet* pb;
						if (window.at(index) == NULL) {
							pb = new Packet(packetSize, &bf);
						} else {
							pb = window.at(index);
						}

						pb->packetNum = packNum;
						pb->seqNum = seqNum;
						pb->setPayload(buffer, sizeRead, true);
						pb->setEof(myFile.eof());
						pb->sent = true;
						if (senderWinSize > 1 && recvWinSize == 1) { // GBN
							pb->timeout = gbnTimeout;
						} else {
							clock_gettime(CLOCK_REALTIME, &pb->timeout);
						}
						// @TODO Add errors
						// Errors will be available as a global map
						// use char* errorType = getErrorType(int sequenceNumber);
						if (pb->errorType != LOST_PACK) {
							sock.sendTo(pb->buildPacket(), packetSize, servAddress, servPort);
						}

						if (window.at(index) == NULL) {
							window.at(index) = pb;
                                                }
						// @TODO: add packet pointer to timeout queue
						timeoutQueue.push(pb);

						cout << "!!!!!!!!! SEND !!!!!!!!!!" << endl;
						cout << "Start: " << expectedSeqNum << " End: " << (expectedSeqNum + senderWinSize - 1) % sequenceNumberRange << endl;
						cout << "Packet: " << packNum << " Seq: " << seqNum << " sent" << endl;
						cout << endl;

						seqNum = (seqNum + 1) % sequenceNumberRange;
						packNum++;

						if (pb->isEof()) {
							done = true;
						}
/*
						// EOF
						if (myFile.eof()) {
							done = true;
							for (unsigned int i = 0; i < window.size(); i++) {
								if ((*window.at(i)).hasReset && !(*window.at(i)).sent) {
									done = false;
									break;
								}
							}
						}
*/
					}
				}
				// unlock modification mutex, but leave sending mutex locked
				pthread_mutex_unlock(&windowMutex);
			}
			// @TODO End Check
		}

		myFile.close();
	} catch (SocketException &e) {
		cerr << e.what() << endl;
		exit(1);
	}
}

// Thread method for recving acks
void* threadRecv (void* ti) {
	// allows thread resources to be reclaimed on termination
	pthread_detach(pthread_self());
	char* ackBuff = new char[packetSize - HEADER_SIZE];
	// Forever! wait for recv.
	for (;;) {
		// Clear ackbuff
		memset(ackBuff, 0, packetSize - HEADER_SIZE);
		// Wait for ack.
		sock.recv(ackBuff, packetSize);
		// build ack packet.
		Packet ack(ackBuff, packetSize, &bf);
		// Wait for windowMutex for threadsafety
		pthread_mutex_lock(&windowMutex);
		// Get index in window.
		int index = ack.seqNum % senderWinSize;
		if (window.at(index) != NULL) {
			// Check for lost ack error
			if ((*window.at(index)).errorType != LOST_ACK) {
				// Set ack in packet
				(*window.at(index)).ackRecieved = true;
				// Print ack
				cout << endl << endl << "Ack " << ack.seqNum << " Recieved" << endl;
				// If the ack is for the first slot in window
				cout << "curr: " << ack.seqNum % senderWinSize << " WindowStart: " << expectedSeqNum << endl;
				if (index == expectedSeqNum % senderWinSize) {
					// Slide the window
					index = expectedSeqNum % senderWinSize;
					while (window.at(index) != NULL && (*window.at(index)).ackRecieved) {
						cout << "Next window: " << (expectedSeqNum + 1) % sequenceNumberRange << " - " << (((expectedSeqNum + 1) % sequenceNumberRange) + senderWinSize - 1) % sequenceNumberRange << endl;
						expectedSeqNum = (expectedSeqNum + 1) % sequenceNumberRange;
						// Reset packet
						(*window.at(index)).reset();
						(*window.at(index)).hasReset = true;

						index = expectedSeqNum % senderWinSize;
					}

					// Unlock thread mutex so that the sender can update the fill the packet and send it.
					pthread_mutex_unlock(&senderMutex);
				} else if (senderWinSize > 1 && recvWinSize == 1) { // Otherwise if you are GBN, set all packets behind you to ack'ed
					// And slide the window
					for (index = 0; index < senderWinSize; index++) { 
						// Check packet number just as an extra precaution
						if (window.at(index) != NULL && (*window.at(index)).sent && (*window.at(index)).packetNum <= (*window.at(ack.seqNum % senderWinSize)).packetNum) {
							cout << "Previous Ack!!!" << endl;
							cout << "Next window: " << (expectedSeqNum + 1) % sequenceNumberRange << " - " << (((expectedSeqNum + 1) % sequenceNumberRange) + senderWinSize - 1) % sequenceNumberRange << endl;
							expectedSeqNum = (expectedSeqNum + 1) % sequenceNumberRange;
							// Reset packet
							(*window.at(index)).reset();
							(*window.at(index)).hasReset = true;
						}
					}

					// Unlock thread mutex so that the sender can update the fill the packet and send it.
					pthread_mutex_unlock(&senderMutex);
				}
			} else { // otherwise remove the error and ignore the ack
				(*window.at(index)).errorType = NO_ERR;
			}


			pthread_mutex_unlock(&windowMutex);
		}
	}

	delete[] ackBuff;

	return NULL;
}

// Thread for handling timeouts.
void* threadTimer(void* ti) {
	// TODO Comments
	// TODO print out where necessary
	
	// When the first packet is sent, start this, perhaps a 1 time use mutex or something
	// First sleep is for timeoutInterval
	// get the timeout time of the top packet, and keep track of it.
	// Use nanosleep or one of the other sleep functions to wait
	// When you wake up from sleep, start a time counter to use later; this counter can be done a few ways
	// for ever
		// If reset == true; set "reset" of packet to false, ignore packet
		// poll and pop from the queue any packet that has an ack or has been reset or are pointers to null
		// resend all packets who did not ack and have timeoutInterval equal to the timeout you got above, then also pop them
		// add all sent packets back into the queue
		// get the timeout for the next packet that was not poped
		// subtract the above stored timeout interval and the counter from it
		// that is the next sleep time
		// set the stored timeout to the timeout of the packet,
		// if sleep time is now less than or = 0, skip sleep
		// else sleep
	// end for
	// all threads should die when the parent dies.  No zombies.

	bool done = false;
	timespec timeInterval;
	timeInterval.tv_sec = 0;
	timeInterval.tv_nsec = timeoutInterval;

	sleep(1);
	for (;;) {
		nanosleep(&timeInterval, &timeInterval);
		while (!timeoutQueue.empty() || !done) {
			timespec currTime;
			clock_gettime(CLOCK_REALTIME, &currTime);
			Packet* top = timeoutQueue.top();

			timespec checkTime = diff((*top).timeout, currTime);
			if (checkTime.tv_sec > 1 || checkTime.tv_nsec > timeoutInterval) {
				// If the packet has been recently reset to blank or an ack has been recieved in the meantime; ignore
				if ((*top).ackRecieved) {
					timeoutQueue.pop();
				} else if ((*top).hasReset) {
					if ((*top).sent) {
						(*top).hasReset = false;
						timeoutQueue.push(top);
						timeoutQueue.pop();
					} else {
						(*top).hasReset = false;
						timeoutQueue.pop();
					}
				} else { // Resend
					cout << "!!!!!!!!! Packet " << (*top).seqNum << " has timed out; resent" << endl;

					// Set new timeout and send
					clock_gettime(CLOCK_REALTIME, &((*top).timeout));
					sock.sendTo((*top).buildPacket(), packetSize, servAddress, servPort);

					timeoutQueue.push(top);
					timeoutQueue.pop();
				}
			} else {
				done = true;
			}
		}

		done = false;
		timeInterval.tv_sec = 0;
	        timeInterval.tv_nsec = timeoutInterval;
	}

	return NULL;
}

timespec diff(timespec start, timespec end)
{
	timespec temp;
	if ((end.tv_nsec-start.tv_nsec)<0) {
		temp.tv_sec = end.tv_sec-start.tv_sec-1;
		temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec;
	} else {
		temp.tv_sec = end.tv_sec-start.tv_sec;
		temp.tv_nsec = end.tv_nsec-start.tv_nsec;
	}
	return temp;
}
